# encoding: utf-8
"""
brightcontent.plugins.archives
"""

import re, os
import sets
import calendar
import time, cStringIO
from datetime import *; from dateutil.relativedelta import *

from dateutil.tz import *
from paste.httpexceptions import *

from brightcontent.core import SLUGCHARS
from brightcontent.util import UrlDispatch, get_base_url, fixup_namespaces, quick_xml_multiscan, web_trigger
from brightcontent.core.store import COMMON_PREFIXES
# from brightcontent.util import get_clean_title
import amara

ARCHIVE_LINK_ITEM_TEMPLATE = '<link rel="prev-archive" href="%s%s/%s/" title="%s, %s"/>\n'
HISTORY_ENV_VAR = 'brightcontent.archives.history'
#XPath expression that can be dynamically invoked by an XSLT processor to process the archives
#requires namespace declarations xmlns:f="http://xmlns.4suite.org/ext" xmlns:bc="http://brightcontent.net/ns/"
ARCHIVE_XSLT_PARSE_EXPRESSION = u'f:parse-xml($bc:%s)'%HISTORY_ENV_VAR

class ArchiveLinks:
    def __init__(self, application, store, app_conf):
        self.application = application
        self.store = store
        self.app_conf = app_conf # temporary until config middleware is working correctly

    def __call__(self, environ, start_response):
        #FIXME: should check the request (e.g. by using UrlDispatch) to be sure that archives are not computed if not needed
        self.build_archive(environ, start_response)
        return self.application(environ, start_response)

    def build_archive(self, environ, start_response):
        archives = sets.Set()
        for atom_updated, atom_id, atom_published, fn in self.store.index:
            archives.add((atom_published.year, atom_published.month))
        history_substrings = [
        '<', HISTORY_ENV_VAR, '>',
        '<!-- Fakery of feed history: http://tools.ietf.org/wg/atompub/draft-nottingham-atompub-feed-history-08.txt -->\n',
        ]
        archives = list(archives)
        archives.sort()
        for year, month in archives:
            history_substrings.append(ARCHIVE_LINK_ITEM_TEMPLATE%(get_base_url(environ), year, month, calendar.month_name[month], year))
        history_substrings.extend(['</', HISTORY_ENV_VAR, '>'])
        history = ''.join(history_substrings)
        environ[HISTORY_ENV_VAR] = history
        environ['brightcontent.archives.parse'] = ARCHIVE_XSLT_PARSE_EXPRESSION
        #print history
        return


"""
Generates a calendar along the lines of:

    <  January, 2007   >
    Mo Tu We Th Fr Sa Su
           1  2  3  4  5
     6  7  8  9 10 11 12
    13 14 15 16 17 18 19
    20 21 22 23 24 25 26
    27 28 29 30 31

Marks present date and those that have entries with archive links

Defines the following classes (for use in CSS customization):

  - bcCalendar
    - calendar table (note: month/year header e.g. January 2007 is in table/th)
  - bcCalendarWeekHeaders
    - week header (Su, Mo, Tu, ...)
  - bcCalendarEmpty
    - filler cell (e.g. days after Jan 31)
  - bcCalendarLive
    - day for which there is an entry (also has links to that day's archives)

And the following IDs:

  - bcCalendarToday
    - today's calendar day
  - bcCalendarSpecificDay
    - specific day being rendered (if any)

Some ideas (e.g. CSS styling of the table) from pycalendar.py by Will Guaraldi
"""

from string import Template

CAL_TEMPLATE = Template('''
<table class="bcCalendar">
  <thead>
    <tr class="bcCalendarTopHeaders">
      $prevmonth<th colspan="5">$monthname, $year</th>$nextmonth
    </tr>
    <tr class="bcCalendarWeekHeaders">
      $dayheaders
    </tr>
  </thead>
  <tbody>
    $month
  </tbody>
</table>
''')

CALENDAR_ENV_VAR = 'brightcontent.calendar'
#XPath expression that can be dynamically invoked by an XSLT processor to process the archives
#requires namespace declarations xmlns:f="http://xmlns.4suite.org/ext" xmlns:bc="http://brightcontent.net/ns/"
CALENDAR_XSLT_PARSE_EXPRESSION = u'f:parse-xml($bc:%s)'%CALENDAR_ENV_VAR

class WeblogCalendar:
    def __init__(self, application, store, app_conf):
        self.application = application
        self.store = store
        self.app_conf = app_conf # FIXME: temporary until config middleware is working correctly
        self.m = UrlDispatch(self.urls)

    def build_calendar(self, environ, start_response, year=2000, month=1, day=1):
        year, month, day = int(year), int(month), int(day)
        baseurl = get_base_url(environ)
        archives = sets.Set()
        
        for atom_updated, atom_id, atom_published, fn in self.store.index:
            archives.add(atom_published.date())

        dayheaders = ''.join(['<td>%s</td>' % dh for dh in calendar.weekheader(3).split()])
        monthcal = calendar.monthcalendar(year, month)
        c = []
        for wk in monthcal:
            c.append('<tr>\n')
            for d in wk:
                d_int = int(d)
                attrs = ''
                if d_int < 1:
                    d = '&#160;'
                    fulldate = date.max #never to be found in archives
                    attrs += ' class="bcCalendarEmpty"'
                else:
                    fulldate = date(year, month, d_int)
                # "today" trumps "specific day"
                if d_int == self._today.day:
                    attrs += ' id="bcCalendarToday"'
                elif d_int == self._specific_day:
                    attrs += ' id="bcCalendarSpecificDay"'
                if fulldate in archives:
                    attrs += ' class="bcCalendarLive"'
                    d = '<a href="%s%i/%i/%s/">%s</a>'%(baseurl, year, month, d, d)
                c.append('\t<td%s>%s</td>\n' % (attrs, d))
            c.append('\n</tr>\n')
        monthname =  calendar.month_name[month]
        prevmonth = date(year, month, day) + relativedelta(months=-1)
        nextmonth = date(year, month, day) + relativedelta(months=+1)
        #Yes, even checking if prevmonth > today, so if someone surfs 3 month in the future, there will be no month nav links
        if prevmonth > self._today:
            prevmonth = ''
        else:
            prevmonth = '<th><a href="%s%i/%i/">&lt;&lt;</a></th>'%(baseurl, prevmonth.year, prevmonth.month)
        if nextmonth > self._today:
            nextmonth = ''
        else:
            nextmonth = '<th><a href="%s%i/%i/">&gt;&gt;</a></th>'%(baseurl, nextmonth.year, nextmonth.month)
        month = ''.join(c)
        environ[CALENDAR_ENV_VAR] = CAL_TEMPLATE.safe_substitute(locals())
        environ['brightcontent.calendar.parse'] = CALENDAR_XSLT_PARSE_EXPRESSION
        return
    
    def __call__(self, environ, start_response):
        path_info = environ.get('PATH_INFO', '/')
        if path_info == '': path_info = '/' # FIXME: just a workaround for a paste bug
        func, query_dict = self.m.match(path_info)
        self._today = date.today()
        self._specific_day = int(query_dict.get('day', 0))
        if not 'year' in query_dict:
            query_dict['year'], query_dict['month'], query_dict['day'] = self._today.year, self._today.month, self._today.day
        if func:
            func(self, environ, start_response, **query_dict)
        return self.application(environ, start_response)

    urls = (
            (r'/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})(/.*)?', build_calendar),
            (r'/(?P<year>\d{4})/(?P<month>\d{1,2})(/.*)?', build_calendar),
            (r'/(?P<year>\d{4})(/.*)?', build_calendar),
            (r'/.*', build_calendar)
        )

